<?php
fps_define('FPS_DB_COUNTER_TABLE', 'index');
fps_define('FPS_DB_COUNTER_KEY', '_id');
fps_define('FPS_DB_COUNTER_VALUE', 'index');
class fpsDB {
	static $COLS_CACHE = array();

	static function getCol($name) {
		if (!isset(self::$COLS_CACHE[$name])){
			$con = new MongoClient(FPS_DB_URL);
			$db = $con->selectDB(FPS_DB_NAME);
			self::$COLS_CACHE[$name] = $db->selectCollection($name);
		}
		return self::$COLS_CACHE[$name];
	}

	static function byId($name, $id){
		$col = self::getCol($name);
		if ($col){
			return $col->findOne(array('_id' => $id));
		}
		else {
			return null;
		}
	}

	static function count($name, $cond=array(), $opts=array()) {
		$col = self::getCol($name);
		if ($col){
			try {
				return $col->count($cond, $opts);
			} catch (Exception $e) {
				var_dump($e);
			}
		}
		else {
			return false;
		}
	}

	static function findOne($name, $cond, $fields=array()) {
		$col = self::getCol($name);
		if ($col){
			return $col->findOne($cond, $fields);
		}
		else {
			return null;
		}
	}

	static function find($name, $cond=array(), $fields=array(), $opts=array()) {
		$col = self::getCol($name);
		if ($col) {
			$cus = $col->find($cond, $fields);
			if (isset($opts['limit'])) {
				$cus->limit($opts['limit']);
			}
			if (isset($opts['skip'])) {
				$cus->skip($opts['skip']);
			}
			if (isset($opts['sort'])) {
				$cus->sort($opts['sort']);
			}
			return $cus;
		}
		else {
			return array();
		}
	}

	static function findList() {
		$cur = call_user_func_array('self::find', func_get_args());
		if (is_array($cur)) {
			return $cur;
		}
		else {
			$list = array();
			foreach ($cur as $item) {
				$list[] = $item;
			}
			return $list;
		}
	}

	static function findListPubJs($name, $param) {
		$cur = call_user_func_array('self::find', [$name, $param["query"], $param["fields"], $param["opts"]]);
		$count = $cur->count();
		$list = array();
		foreach ($cur as $item) {
			$list[] = $item;
		}

		return array('items'=> $list, 'total'=> $count);
	}

	static function filterWord(&$param, $data, $fields = array(), $multiple=false) {
		if(isset($data["word"]) && $data['word'])
		{
			$word = trim($data['word']);

			if(is_numeric($word)) {
				$param['query']['_id'] = intval($word);
			}
			else
			{
				$or = array();
				if ($multiple) {
					$words = preg_split('/\s+/', $word);
					$words = array_map('preg_quote', $words);
					$regx = self::regx('/'.implode('|', $words).'/i');
				}
				else {
					$regx = self::regx('/{word}/i', $word);
				}

				foreach ($fields as $field)
				{
					array_push($or, array($field => $regx));
				}
			}
		}

		switch (count($or)) {
			case 0:
				break;
			case 1:
				$param['query'] = array_merge($param['query'], $or[0]);
				break;
			default:
				$param['$or'] = $or;
				break;
		}
	}

	static function filterCommon(&$param, &$data) {
		if(!isset($data["limit"]))
		{
			$data['limit'] = 20;
		}
		if(!isset($data["page"]))
		{
			$data['page'] = 1;
		}
		if(!isset($data["sort"]))
		{
			$data['sort'] = '_id|-1';
		}

		$param['opts']['sort'] = [];
		$arr = explode(',', $data['sort']);
		foreach ($arr as $sort)
		{
			$sort = explode('|', $sort);
			if(count($sort) == 2)
			{
				$param['opts']['sort'][$sort[0]] = intval($sort[1]);
			}
		}

		$param['opts']['limit'] = intval($data['limit']);
		$param['opts']['skip'] = ($data['page'] -1) * $data['limit'];
	}

	static function filterCreateTime(&$param, $data, $field = 'createTime') {
		$param['query'][$field] = [];
		if(isset($data["begindate"]))
		{
			$param['query'][$field]['$gte'] = intval($data["begindate"]);
		}
		if(isset($data["enddate"]))
		{
			$param['query'][$field]['$lte'] = intval($data["enddate"]);
		}

	}

	static function aggregate($name, $pipeline, $opts=array()) {
		$col = self::getCol($name);
		if ($col) {
			$rs = $col->aggregate($pipeline, $opts);
			if ($rs['ok'] == 1) {
				return $rs['result'];
			}
		}
		return false;
	}

	static function index($type, $inc_value = 1) {
		if ($inc_value <= 0) {
			$inc_value = 1;
		}
		$col = self::getCol(FPS_DB_COUNTER_TABLE);
		$rs = $col->findAndModify(
			array(FPS_DB_COUNTER_KEY => $type),
			array('$inc' => array(FPS_DB_COUNTER_VALUE => $inc_value)),
			null,
			array('new' => true, 'upsert' => true)
		);
		if ($rs) {
			return $rs[FPS_DB_COUNTER_VALUE];
		}
		else {
			return false;
		}
	}

	static function regx($patten, $word=null) {
		if (!is_null($word)) {
			$patten = str_replace('{word}', preg_quote($word), $patten);
		}

		return new MongoRegex($patten);
	}

	static function save($name, $data, $upsert=true) {
		if (isset($data['_id']) && $data['_id'] !== false) {
			$cond = array('_id' => $data['_id']);
			$col = self::getCol($name);
			if ($col) {
				unset($data['_id']);
				switch (true) {
					case isset($data['$inc']):
						break;
					default:
						$data = array('$set' => $data);
						break;
				}
				$res = $col->update($cond, $data, array('upsert'=>$upsert));
				return ($res ? $res['ok'] > 0 : false);
			}
		}
		return false;
	}

	static function update($name, $cond, $data, $opts=array()) {
		$col = self::getCol($name);
		if ($col) {
			return $col->update($cond, $data, $opts);
		}
		else {
			return false;
		}
	}

	static function updateAll($name, $cond, $data, $opts=array()) {
		$col = self::getCol($name);
		if ($col) {
			$opts['multiple'] = true;
			return $col->update($cond, $data, $opts);
		}
		else {
			return false;
		}
	}

	static function findAndModify($name, $cond, $data, $fields=[], $opts=[]) {
		$col = self::getCol($name);
		if ($col) {
			try {
				return $col->findAndModify($cond, $data, $fields, $opts);
			}
			catch (Exception $e) {
				return false;
			}
		}
		else {
			return false;
		}
	}

	static function remove($name, $cond, $opts=[]) {
		$col = self::getCol($name);
		if ($col) {
			return $col->remove($cond, $opts);
		}
		else {
			return false;
		}
	}

	static function table($name) {
		return new fpsDBTable($name);
	}
}

class fpsDBTable {
	private $name = null;

	function __construct($name) {
		$this->name = $name;
	}

	function __call($method, $params) {
		if (method_exists('fpsDB', $method)) {
			array_unshift($params, $this->name);
			return call_user_func_array(['fpsDB', $method], $params);
		}
		else {
			throw new Exception("fpsDB::{$method}() method not exists.", 400);
		}
	}
}
